using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;
using System.Xml.Linq;
using Mysoft.ConfigCenterClient.Tools.Encryption.SM4;
using NacosDecryptTool.Config;
using Newtonsoft.Json;

namespace NacosDecryptTool
{
    /**
     * openapi
     * v1: https://nacos.io/docs/v1/open-api/
     * v2.3: https://github.com/nacos-group/nacos-group.github.io/blob/develop-astro-nacos/src/content/docs/v2.3/zh-cn/guide/user/open-api.md#1.1
     * v2.5: https://github.com/nacos-group/nacos-group.github.io/blob/develop-astro-nacos/src/content/docs/v2.5/zh-cn/manual/user/open-api.md
     */
    public class NacosHelper
    {
        private AppSettings _appSettings;
        private readonly HttpClient _httpClient;
        private readonly string _baseUrl;

        private string _accessToken;

        public NacosHelper(AppSettings appSettings)
        {
            this._appSettings = appSettings;
            _httpClient = new HttpClient();
            _baseUrl = $"{appSettings.Nacos.Server}/nacos/v1/cs";

            getAccessToken(appSettings);
        }

        private void getAccessToken(AppSettings appSettings)
        {
            // Login to get access token
            var loginContent = new FormUrlEncodedContent(new[]
            {
                new KeyValuePair<string, string>("username", appSettings.Nacos.Username),
                new KeyValuePair<string, string>("password", appSettings.Nacos.Password)
            });

            var loginResponse = _httpClient.PostAsync($"{appSettings.Nacos.Server}/nacos/v1/auth/login", loginContent)
                .Result;
            loginResponse.EnsureSuccessStatusCode();

            var loginResult = JsonConvert.DeserializeObject<Dictionary<string, object>>(
                loginResponse.Content.ReadAsStringAsync().Result);
            _accessToken = loginResult["accessToken"].ToString();
        }

        public async Task<List<NacosConfigDetail>> ListAllDataIdsAsync()
        {
            string sourceNamespace = _appSettings.Nacos.SourceNamespace;
            var response = await _httpClient.GetAsync(
                $"{_baseUrl}/configs?dataId=&group=&appName=&config_tags=&pageNo=1&pageSize=999&tenant={sourceNamespace}&types=&search=blur&accessToken={_accessToken}");
            response.EnsureSuccessStatusCode();

            var content = await response.Content.ReadAsStringAsync();
            var result = JsonConvert.DeserializeObject<NacosConfigResponse>(content);
            return result.PageItems;
        }

        public Dictionary<string, string> DecryptConfigValues(Dictionary<string, string> encryptedConfig)
        {
            var decryptedConfig = new Dictionary<string, string>();
            foreach (var kvp in encryptedConfig)
            {
                var decrypt = SM4Helper.Decrypt_CBC(_appSettings.SM4.Key, false, _appSettings.SM4.IV, kvp.Value, 2);
                decryptedConfig[kvp.Key] = decrypt;
            }

            return decryptedConfig;
        }

        public async Task EnsureNamespace(string namespaceId)
        {
            //确认nacos中是否存在此namespace，如果没有，需要创建
            var response = await _httpClient.GetAsync(
                $"{_appSettings.Nacos.Server}/nacos/v1/console/namespaces?namespaceId={namespaceId}&accessToken={_accessToken}");
            response.EnsureSuccessStatusCode();

            if (response.StatusCode == System.Net.HttpStatusCode.OK)
            {
                var content = await response.Content.ReadAsStringAsync();
                var result = JsonConvert.DeserializeObject<NacosNamespaceResponse>(content);
                if (result.Data.Count(x => x.Namespace.Equals(namespaceId)) == 0)
                {
                    //创建namespace
                    var createNamespaceContent = new FormUrlEncodedContent(new[]
                    {
                        new KeyValuePair<string, string>("namespaceName", _appSettings.Nacos.TargetNamespace),
                        new KeyValuePair<string, string>("customNamespaceId", _appSettings.Nacos.TargetNamespace),
                        new KeyValuePair<string, string>("namespaceDesc",
                            _appSettings.Nacos.SourceNamespace + "_decrypt"),
                    });
                    var createNamespaceResponse = await _httpClient.PostAsync(
                        $"{_appSettings.Nacos.Server}/nacos/v1/console/namespaces?accessToken={_accessToken}",
                        createNamespaceContent);
                    createNamespaceResponse.EnsureSuccessStatusCode();
                    Console.WriteLine($"创建namespace成功：{namespaceId}");
                }
            }
        }

        public async Task PublishConfigAsync(string dataId, string group, string namespaceId,
            Dictionary<string, string> config)
        {
            var content = string.Join("\n", config.Select(kvp => $"{kvp.Key}={kvp.Value.Replace("\n","")}"));
            if (String.IsNullOrEmpty(content))
            {
                content = "# empty file";
            }

            var formContent = new FormUrlEncodedContent(new[]
            {
                new KeyValuePair<string, string>("type", "properties"),
                new KeyValuePair<string, string>("dataId", dataId),
                new KeyValuePair<string, string>("group", group),
                new KeyValuePair<string, string>("content", content),
                new KeyValuePair<string, string>("tenant", namespaceId)
            });

            try
            {
                var response =
                    await _httpClient.PostAsync($"{_baseUrl}/configs?accessToken={_accessToken}", formContent);
                response.EnsureSuccessStatusCode();
                await Task.Delay(500);
            }
            catch (Exception e)
            {
                Console.WriteLine(e); 
            }
        }
        
        public async Task PublishConfigAsync(string dataId, string group, string namespaceId, string content, string type)
        {

            var formContent = new FormUrlEncodedContent(new[]
            {
                new KeyValuePair<string, string>("type", type),
                new KeyValuePair<string, string>("dataId", dataId),
                new KeyValuePair<string, string>("group", group),
                new KeyValuePair<string, string>("content", content),
                new KeyValuePair<string, string>("tenant", namespaceId)
            });

            try
            {
                var response =
                    await _httpClient.PostAsync($"{_baseUrl}/configs?accessToken={_accessToken}", formContent);
                response.EnsureSuccessStatusCode();
                await Task.Delay(500);
            }
            catch (Exception e)
            {
                Console.WriteLine(e); 
            }
        }

        public async Task MigrateConfigsAsync()
        {
            string sourceNamespace = _appSettings.Nacos.SourceNamespace;
            string targetNamespace = _appSettings.Nacos.TargetNamespace;
            EnsureNamespace(targetNamespace);

            var dataIds = await ListAllDataIdsAsync();

            foreach (var config in dataIds)
            {
                String content = config.Content.Trim();

                try
                {
                    String configType = config.Type;
                    if (config.Type == null)
                    {
                        //根据content的内容，自动识别格式
                        configType = DetectContentFormat(content);
                    }

                    if (configType == "properties")
                    {
                        await ProcessPropertites(config, targetNamespace);
                    }
                    // else if (configType == "yaml")
                    // {
                    //     //发布
                    //     await PublishConfigAsync(config.DataId, config.Group, targetNamespace, config.Content);
                    // }
                    // else if (configType == "json")
                    // {
                    //     //发布
                    //     await PublishConfigAsync(config.DataId, config.Group, targetNamespace, config.Content);
                    // }
                    else
                    {
                        //Console.WriteLine($"不支持的配置类型：{configType}");
                        
                       //  //content 是否为base64字符串？
                       // bool isBase64 = Convert.TryFromBase64String(content, new byte[]{}, out int bytes);
                       // if (isBase64)
                       try
                       {
                           //解密
                           var decrypt = SM4Helper.Decrypt_CBC(_appSettings.SM4.Key, false, _appSettings.SM4.IV,
                               content, 2);
                           //发布
                           await PublishConfigAsync(config.DataId, config.Group, targetNamespace, decrypt, configType);
                       }
                       catch (Exception e)
                       {
                           try
                           {
                               //发布
                               await PublishConfigAsync(config.DataId, config.Group, targetNamespace, content, configType);
                           }
                           catch (Exception exception)
                           {
                               Console.WriteLine(exception);
                           }
                       }
                    }
                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                    throw;
                }
            }
        }

        public static string DetectContentFormat(string content)
        {
            content = content.Trim();

            // 检测JSON格式
            if (content.StartsWith("{") && content.EndsWith("}") ||
                content.StartsWith("[") && content.EndsWith("]"))
            {
                try
                {
                    JsonDocument.Parse(content);
                    return "json";
                }
                catch
                {
                }
            }

            // 检测YAML格式
            if (content.Contains(": ") && (content.Contains("\n") || content.Split('\n').Length > 1))
            {
                return "yaml";
            }

            // 检测XML格式
            if (content.StartsWith("<") && content.EndsWith(">"))
            {
                try
                {
                    XDocument.Parse(content);
                    return "xml";
                }
                catch
                {
                }
            }

            // 检测Properties格式
            if (content.Split('\n', StringSplitOptions.RemoveEmptyEntries)
                .Any(line => line.Contains('=') && !line.Trim().StartsWith("#") && line.Split('=', StringSplitOptions.RemoveEmptyEntries).Length>=2))
            {
                return "properties";
            }

            return "text";
        }

        private async Task ProcessPropertites(NacosConfigDetail config, string targetNamespace)
        {
            string content = config.Content;
            //处理content内容，并分割成key-value对
            var properties = ParseProperties(content);

            //解密
            var decryptedConfig = DecryptConfigValues(properties);

            //发布
            await PublishConfigAsync(config.DataId, config.Group, targetNamespace, decryptedConfig);

            Console.WriteLine($"Migrated {config.DataId} successfully");
        }

        private Dictionary<string, string> ParseProperties(string content)
        {
            var properties = new Dictionary<string, string>();

            foreach (var line in content.Split(new[] {'\n', '\r'}, StringSplitOptions.RemoveEmptyEntries))
            {
                //移除type=properties格式的注释信息
                if (line.StartsWith("#"))
                {
                    continue;
                }

                var parts = line.Split(new[] {'='}, 2);
                if (parts.Length == 2)
                {
                    var value = parts[1].Trim();
                    if (value.Contains("#"))
                    {
                        value = value.Substring(0, value.IndexOf("#"));
                    }

                    properties[parts[0].Trim()] = value.Trim();
                }
            }

            return properties;
        }
    }
}